package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import server.app.Context;
import server.app.Host;
import server.app.Mapper;
import server.app.Wrapper;
import server.service.MappedConnect;
import server.service.MappedEngine;
import server.service.MappedServerInfo;
import server.service.MappedService;
import server.util.DiskClassLoader;

import java.io.File;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 *MiniCat的主类
 */
public class BootStrap {
    private int port = 8080;
    private Map<String, HttpServlet> servletMap = new HashMap<>();

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public static void main(String[] args) {
        BootStrap bootStrap = new BootStrap();
        try {
            bootStrap.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void start() throws Exception {
        // 加载解析相关的配置，web.xml
        MappedServerInfo serverInfo = loadServerXml();

        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handle = new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handle);


        ServerSocket serverSocket = new ServerSocket(serverInfo.getService().getConnects()[0].getPort());
        System.out.println("======>>>>MiniCat start on port:" + serverInfo.getService().getConnects()[0].getPort());
/*        // 1.0版本
          // 浏览器请求地址localhost:8080，页面输出Hello Conn's Minicat
        while (true) {
            Socket s = serverSocket.accept();
            // 接收Socket请求，获取输出流
            OutputStream outputStream = s.getOutputStream();
            String data = "Hello Conn's MiniCat!";
            // 定义成功请求响应头
            String responseText = server.HttpProtocolUtil.getHttpHeader200(data.getBytes().length) + data;
            outputStream.write(responseText.getBytes());
            s.close();
        }*/
        // 2.0版本
        // 封装request和response对象，返回静态html页面
/*        while (true) {
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            server.Request request = new server.Request(inputStream);
            server.Response response = new server.Response(socket.getOutputStream());
            response.outputHtml(request.getUrl());
            socket.close();
        }*/

        // 3.0版本
        // 可以动态请求资源(server.Servlet)
/*        while(true) {
            Socket accept = serverSocket.accept();
            InputStream inputStream = accept.getInputStream();
            if(inputStream.available() == 0) {
                accept.close();
                continue;
            }
            Request request = new Request(inputStream);
            Response response = new Response(accept.getOutputStream());
            // 静态资源处理
            if (servletMap.get(request.getUrl()) == null) {
                response.outputHtml(request.getUrl());
            } else {
                // 动态Servlet请求
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request, response);
            }
            accept.close();
        }*/
    /*
    * 多线程改造
    * */
/*    while (true) {
        Socket socket = serverSocket.accept();
        RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
        requestProcessor.start();
    }*/
        System.out.println("===============>>>>>>>使用线程池进行多线程改造");
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, serverInfo.getService().getEngine().getMapper());
            threadPoolExecutor.execute(requestProcessor);
        }
    }

    private MappedServerInfo loadServerXml() throws DocumentException {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        MappedService service = new MappedService();
        MappedServerInfo serverInfo = new MappedServerInfo(service);
        SAXReader saxReader = new SAXReader();

        Document document = saxReader.read(resourceAsStream);
        Element rootElement = document.getRootElement();
        List<Element> list = rootElement.selectNodes("//Service");
        for (int i = 0; i < list.size(); i++) {
            Element serviceElement = list.get(i);
            List<Element> connectorElement = serviceElement.selectNodes("//Connector");
            if (connectorElement != null && connectorElement.size() > 0) {
                service.init(connectorElement.size());
                for (Element connectorNode :
                     connectorElement) {
                    String port = connectorNode.attributeValue("port");
                    MappedConnect connect = new MappedConnect(Integer.parseInt(port));
                    service.addConnect(connect);
                }
            }

            Element engineElement = (Element) serviceElement.selectSingleNode("Engine");
            if (null != engineElement) {
                MappedEngine engine = new MappedEngine();
                service.setEngine(engine);

                Mapper mapper = new Mapper();
                engine.setMapper(mapper);
                List<Element> hostsElement = engineElement.selectNodes("//Host");
                if ( null != hostsElement && 0 < hostsElement.size()) {
                    mapper.init(hostsElement.size());
                    for (Element hostNode:
                         hostsElement) {
                        Host host = new Host();
                        mapper.addHost(host);
                        host.setName(hostNode.attributeValue("name"));
                        String appBase = hostNode.attributeValue("appBase");
                        File file = new File(appBase);
                        if (file.exists()) {
                            File[] apps = file.listFiles();
                            for(File app : apps) {
                                if (app.isFile()) {
                                    continue;
                                }
                                Context context = new Context(app.getName());
                                host.addContext(context);
                                String webXmlPath = app.getPath().replaceAll("\\\\", "/") + "/web.xml";
                                loadServletXml(webXmlPath, context);
                            }
                        }
                    }
                }
            }

        }
        return serverInfo;
    }
    /**
     *  加载解析web.xml文件，初始化servlet
     */
    private void loadServletXml(String webXmlPath, Context context) {
        File file = new File(webXmlPath);
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(file);
            Element rootElement = document.getRootElement();
            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            context.init(selectNodes.size());
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element = selectNodes.get(i);
                Element servletNameNodes = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletNameNodes.getStringValue();
                Element servletClassNodes = (Element) element.selectSingleNode("servlet-class");
                String servletCalss = servletClassNodes.getStringValue();
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                Wrapper wrapper = new Wrapper(urlPattern, (HttpServlet) DiskClassLoader.loadClass(webXmlPath.replace("/web.xml", ""), servletCalss).newInstance());
                context.addWrapper(wrapper);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}
